#pragma once

#include <iostream>
#include <stdint.h>
#include <sys/socket.h>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#include "log.hpp"

enum {
    SOCKETERR = 2,
    BINDERR,
    LINSTENERR
};

const int backlog = 10;

class Sock
{
public:
    Sock()
    {}
    ~Sock()
    {}
    // 创建套接字
    void Socket()
    {
        // SOCK_STREAM支持listen操作
        _sockfd = socket(AF_INET,SOCK_STREAM,0);
        if(_sockfd < 0)
        {
            lg(Fatal,"socket error, %s: %d",strerror(errno),errno);
            exit(SOCKETERR);
        }
        int opt = 1;
        setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    }
    // 绑定套接字
    void Bind(uint16_t port)
    {
        struct sockaddr_in local;
        memset(&local,0,sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        // INADDR_ANY: 是一个特定的IP地址，在IPv4中是0.0.0.0
        // 这个常量用于告诉网络套接字在绑定操作时，可以接受来自任何网络接口的连接请求
        local.sin_addr.s_addr = INADDR_ANY;

        if(bind(_sockfd,(struct sockaddr*)&local,sizeof(local)) < 0)
        {
            lg(Fatal, "bind error, %s: %d", strerror(errno),errno);
            exit(BINDERR);
        }
    }
    // 监听
    void Listen()
    {        
        if(listen(_sockfd,backlog) < 0)
        {
            lg(Fatal,"listen error,%s: %d",strerror(errno),errno);
            exit(LINSTENERR);
        }
    }
    // 请求连接
    int Accept(std::string &clientip,uint16_t &clientport)
    {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        int newfd = accept(_sockfd,(struct sockaddr*)&client,&len);
        if(newfd < 0)
        {
            lg(Warning, "accept error, %s:%d",strerror(errno),errno);
            return -1;
        }
        clientip = inet_ntoa(client.sin_addr);
        clientport = ntohs(client.sin_port);
                
        return newfd;
    }
    // 连接
    bool Connect(const std::string &ip,const uint16_t &port)
    {
        struct sockaddr_in peer;
        memset(&peer,0,sizeof(peer));
        peer.sin_family = AF_INET;
        peer.sin_port = htons(port);
        peer.sin_addr.s_addr = inet_addr(ip.c_str());

        int n = connect(_sockfd,(struct sockaddr*)&peer,sizeof(peer));
            lg(Fatal, "bind error, %s: %d", strerror(errno),errno);

        if(n == -1)
        {
            std::cerr << "connect to " << ip << ":" << port << " error" << std::endl;
            return false;
        }

        return true;
    }
    void Close()
    {
        close(_sockfd);
    }
    int Fd()
    {
        return _sockfd;
    }
private:
    int _sockfd;
};